﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Input;

namespace MapLibrary
{
    class TransformationManager
    {

        private const float ROTATION_CONVERSION = 360.0f / 10000.0f;

        /**
         * These are the three actions you can perform on a EditorNode
         */
        enum Actions { None, Move, Rotate, Scale };

        /**
         * Action that is currently being performed
         */
        private int mActiveAction;

        /**
         * The start position of the mouse for any action
         */
        private Vector2 mStartPosition;

        /**
         * The node an action is being performed on
         */
        private ITransformable mSelectedNode;

        /**
         * Stores the original displacement between the center of the
         * node and the original mouse click.
         */
        private Vector2 mOriginalDisplacement;

        /**
         * Stores the original rotation of a box before the
         * action was performed on it.
         */
        private float mOriginalRotation;

        /**
         * Stores the original scale of a box before the
         * action was performed on it.
         */
        private Vector2 mOriginalScale;

        /**
         * Viewport to handle movement transformations
         */
        private WorldViewport mViewport;

        public TransformationManager(WorldViewport pViewport)
        {
            mActiveAction = (int)Actions.None;
            mViewport = pViewport;
            mOriginalDisplacement = new Vector2();
        }

        /**
         * Gets called if the action is a move action.  Moves the node to the
         * cursor position.
         */
        public void UpdateMoveAction(GameInput pInput)
        {
            Vector2 tMousePosition = pInput.GetMousePosition();
            Vector2 nodeCenter = mSelectedNode.GetCenter();

            Vector2 tMouseWorldCoords = mViewport.ViewportCoordsToWorldCoords(tMousePosition);

            tMouseWorldCoords.X = tMouseWorldCoords.X + mOriginalDisplacement.X;
            tMouseWorldCoords.Y = tMouseWorldCoords.Y + mOriginalDisplacement.Y;

            mSelectedNode.SetPosition(tMouseWorldCoords);
        }

        /**
         * Gets called if the action is a rotation action.  Rotates
         * the node depending on cursor location relative to the
         * start cursor position.
         */
        public void UpdateRotateAction(GameInput pInput)
        {
            Vector2 tMousePosition = pInput.GetMousePosition();

            float xPrime = tMousePosition.X - mStartPosition.X;
            float yPrime = tMousePosition.Y - mStartPosition.Y;

            double distance = Math.Sqrt((xPrime * xPrime) + (yPrime * yPrime));

            float rotationDiff = (float)(distance * ROTATION_CONVERSION);

            if (xPrime >= 0)
                mSelectedNode.SetRotation(mOriginalRotation + rotationDiff);
            else
                mSelectedNode.SetRotation(mOriginalRotation - rotationDiff);
        }

        /**
         * Gets called if the action is a scaling action.  Scales
         * the node based of the displacement of the cursor from its
         * start position.  Rotates this vector in the direction of the
         * node so scaling makes sense to the user.
         */
        public void UpdateScaleAction(GameInput pInput)
        {
            Vector2 tMousePosition = pInput.GetMousePosition();

            float xPrime = tMousePosition.X - mStartPosition.X;
            float yPrime = tMousePosition.Y - mStartPosition.Y;
            float rotation = mSelectedNode.GetRotation();

            double xRot = (xPrime * Math.Cos(rotation) + yPrime * Math.Sin(rotation));
            double yRot = (-xPrime * Math.Sin(rotation) + yPrime * Math.Cos(rotation));

            float scaleDiffX = (float)(xRot / 100.0f);
            float scaleDiffY = (float)(yRot / 100.0f);

            Vector2 NewScale = new Vector2();
            NewScale.X = mOriginalScale.X + scaleDiffX;
            NewScale.Y = mOriginalScale.Y + scaleDiffY;

            if (NewScale.X < 0.05f)
                NewScale.X = .1f;

            if (NewScale.Y < 0.1f)
                NewScale.Y = .1f;

            mSelectedNode.SetScale(NewScale);
        }

        public void Update(GameInput pInput, ITransformable pHoverNode)
        {
            int currentAction = GetActivatedAction(pInput);

            if (mActiveAction == currentAction)
            {
                switch (mActiveAction)
                {
                    case (int)Actions.Move:
                        UpdateMoveAction(pInput);
                        break;
                    case (int)Actions.Rotate:
                        UpdateRotateAction(pInput);
                        break;
                    case (int)Actions.Scale:
                        UpdateScaleAction(pInput);
                        break;
                    default:
                        break;
                }
            }
            else
            {
                if (pHoverNode != null)
                {
                    mActiveAction = currentAction;
                    mStartPosition = pInput.GetMousePosition();
                    mSelectedNode = pHoverNode;

                    mOriginalRotation = mSelectedNode.GetRotation();
                    mOriginalScale = mSelectedNode.GetScale();

                    Vector2 tWorldStartCoords = mViewport.ViewportCoordsToWorldCoords(mStartPosition);
                    mOriginalDisplacement.X = mSelectedNode.GetPosition().X - tWorldStartCoords.X;
                    mOriginalDisplacement.Y = mSelectedNode.GetPosition().Y - tWorldStartCoords.Y;
                }
                else
                    mActiveAction = (int)Actions.None;
            }
        }

        private int GetActivatedAction(GameInput pInput)
        {
            bool leftMouseButton = pInput.IsLeftMouseButtonDown();
            bool rightMouseButton = pInput.IsRightMouseButtonDown();
            bool shift = pInput.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftShift);

            if (leftMouseButton)
                return (int)Actions.Move;

            if (rightMouseButton && shift)
                return (int)Actions.Scale;

            if (rightMouseButton && !shift)
                return (int)Actions.Rotate;

            return (int)Actions.None;
        }
    }
}
